from PyQt4 import QtCore, QtGui
import ConfigParser
import ctypes
import itunes
import pyHook
import sys
import util
import win32con
import threading

#Visible working area for the screen
SPI_GETWORKAREA = 48
    
#Hotkey IDs
"""
NEXT_TRACK = 10
PREV_TRACK = 20
PLAY_PAUSE = 30
STOP = 40
SHOW_INFO = 50
SHUFFLE = 60
MOD_ALT = 80
MOD_SHIFT = 70
MOD_CONTROL = 90
MOD_WIN = 100
"""


MYEVENT = QtCore.QEvent.User
class MyCustomEvent(QtCore.QEvent):
    def __init__(self, ddict={}):
        self.dict = ddict
        QtCore.QEvent.__init__(self, MYEVENT)
    def type(self):
        print "called"
        return MYEVENT

class CRect(ctypes.Structure):
    _fields_ = [('left',ctypes.c_ulong),
                ('top',ctypes.c_ulong),
                ('right',ctypes.c_ulong),
                ('bottom',ctypes.c_ulong)]

class InfoLabel(QtGui.QLabel):
    def __init__(self, parent):
        QtGui.QLabel.__init__(self, ''.join(("Name<br><font color='#CCCCCC'>",
                        "Artist<br><i>Album</i></font>")), parent)
        self.clicked = None
        self.setContentsMargins(3, 3, 3, 3)
        self.palette().setColor(QtGui.QPalette.Foreground, QtGui.QColor('white'))
        self.setFont(QtGui.QFont("Arial", 8))
        self.adjustSize()
        
    def set_track(self, track=None):
        if track is None:
            return
        text = ''.join((track.name,
                        "<br><font color='#CCCCCC'>",
                        track.artist,
                        "<br><i>",
                        track.album if len(track.album) != 0 else "Unknown Album",
                        "</i></font>"))
        self.setText(text)
        self.adjustSize()
    
    def mousePressEvent(self, e):
        if self.clicked is not None:
            self.clicked.__call__(e)

class Systray(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        
        #make our main widget the info panel
        self.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.X11BypassWindowManagerHint 
                            | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.FramelessWindowHint)
        self.palette().setColor(QtGui.QPalette.Background, QtGui.QColor("#333333"))
        
        #some stuff that we will use later
        self.screen_right = None
        self.screen_bottom = None
        self.label = None
        self.track = None
        self.modifiers = 0
        self.hotkeys_down = []
        
        #HACK: ROUGH WORKAROUND FOR LOW LEVEL EVENT ISSUE... PAIN IN THE FUCKING REAR
        
        #setup system tray application
        self._create_tray_icon()
        self.trayIcon.show()
        
        #load program settings
        self.config = ConfigParser.ConfigParser()
        self.config.read("settings.cfg")
        
        #set up pyHook
        self._setup_hotkeys()
        
        #prepare itunes handlers 
        itunes.track_handler = self.show_info
        itunes.exit_handler = self.cleanup
        
        #display current track
        self.show_info(forced=True)

    def cleanup(self, e=None):
        self.trayIcon.hide()
        del self.hook_mgr
        QtGui.qApp.quit() 
    
    def show_info(self, track=None, forced=False):
        if forced:
            self.track = track = itunes.com2track(itunes.app.CurrentTrack)
        if track is None:
            return            
        if self.track != track or forced:
            self.track = track
            if self.label is None: 
                self._create_label() #lazily create
            self.label.set_track(track) #set the text
            if self.screen_bottom is None or self.screen_right is None:
                self._update_work_area() #lazily find work area
            #reposition the info box
            width = max(100, self.label.width()+20)
            height = self.label.height()
            x = self.screen_right - width - 3
            y = self.screen_bottom - height - 3
            self.setGeometry(x, y, width, height)
            #show the info box
            self.show()
            self.timer.start(2500)
            
    def shuffle(self):
        if itunes.app.CanSetShuffle:
                itunes.app.CurrentPlaylist.Shuffle = not itunes.app.CurrentPlaylist.Shuffle
    
    def next_track(self):
        itunes.app.NextTrack()
        itunes.app.Play()
        if not itunes.event_success:
            self.show_info(forced=True)
    
    def previous_track(self):
        itunes.app.PreviousTrack()
        itunes.app.Play()
        if not itunes.event_success:
            self.show_info(forced=True)
    
    def play_pause(self):
        itunes.app.PlayPause()
    
    def stop(self):
        itunes.app.Stop()
    
    def _create_tray_icon(self):
        self.infoAction = self._action(self.tr("&Show Track Info"), self._tray_icon_activated)
        self.shuffleAction = self._action(self.tr("&Toggle Shuffle"), self.shuffle)
        self.playPauseAction = self._action(self.tr("&Play/Pause"), self.play_pause)
        self.nextAction = self._action(self.tr("&Next Track"), self.next_track)
        self.prevAction = self._action(self.tr("&Previous Track"), self.previous_track)
        self.stopAction = self._action(self.tr("&Stop"), self.stop)
        self.quitAction = self._action(self.tr("&Quit"), self.cleanup)
                
        self.trayIconMenu = QtGui.QMenu(self)
        self.trayIconMenu.addAction(self.infoAction)
        self.trayIconMenu.addAction(self.shuffleAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.playPauseAction)
        self.trayIconMenu.addAction(self.nextAction)
        self.trayIconMenu.addAction(self.prevAction)
        self.trayIconMenu.addAction(self.stopAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)
        
        self.trayIcon = QtGui.QSystemTrayIcon(self)
        self.trayIcon.setToolTip("iShortcut")
        self.trayIcon.setIcon(QtGui.QIcon("icon.png"))
        self.trayIcon.setContextMenu(self.trayIconMenu)
        
        self.connect(self.trayIcon, 
                     QtCore.SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), 
                     self._tray_icon_activated)
    
    def _action(self, text, slot):
        act = QtGui.QAction(self.tr(text), self)
        QtCore.QObject.connect(act, QtCore.SIGNAL("triggered()"), slot)
        return act 
    
    def _setup_hotkeys(self):
        #input map to have global keys only registered once
        """
        self.key_input = {
                ID_NEXT_TRACK:False,
                ID_PREV_TRACK:False,
                ID_PLAY_PAUSE:False,
                ID_STOP:False,
                ID_SHOW_INFO:False,
                ID_SHUFFLE:False }
        """
        
        #gets hotkeys from settings
        self.NextTrack = util.hotkey(self.config.get("Hotkeys", "NextTrack"))
        self.PrevTrack = util.hotkey(self.config.get("Hotkeys", "PrevTrack"))
        self.PlayPause = util.hotkey(self.config.get("Hotkeys", "PlayPause"))
        self.Stop = util.hotkey(self.config.get("Hotkeys", "Stop"))
        self.ShowInfo = util.hotkey(self.config.get("Hotkeys", "ShowInfo"))
        self.Shuffle = util.hotkey(self.config.get("Hotkeys", "Shuffle"))
        
        #set up global hotkeys
        self.hook_mgr = pyHook.HookManager()
        #watch for key events
        self.hook_mgr.KeyDown = self.global_key_down
        self.hook_mgr.KeyUp = self.global_key_up
        #set the hook
        self.hook_mgr.HookKeyboard()
    
    def _create_label(self):
        self.label = InfoLabel(self)
        self.label.clicked = self.mousePressEvent
        self.timer = QtCore.QTimer()
        self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.on_timer)
    
    def _update_work_area(self):
        #Find windows work area
        r = CRect()
        x = ctypes.windll.user32
        x.SystemParametersInfoA(SPI_GETWORKAREA,
                                0, ctypes.byref(r), 0)
        self.screen_right = int(r.right)
        self.screen_bottom = int(r.bottom)
        r = x = None
    
    def on_timer(self):
        self.timer.stop()
        self.hide()
    
    def mousePressEvent(self, e):
        self.hide()

    def _tray_icon_activated(self, reason=None):
        if reason is None or reason != 1:
            self.show_info(forced=True)
    
    #CHECKS HOTKEYS...
    def _check_hotkey(self, id, hotkey, pressing):
        if id == hotkey[0] and self.modifiers == hotkey[1]: #same key
            if hotkey[2] != pressing:
                hotkey[2] = pressing
                if pressing: #first press, run hotkey
                    self.hotkeys_down.append(hotkey)
                    print self.receiver
                    #ddict = {'key':hotkey}
                    #QtCore.QThread.postEvent(self.receiver, MyCustomEvent(ddict))
                    #self._run_hotkey(h)
            return True
        return False
    
    def _run_hotkey(self, hotkey):
        if hotkey == self.ShowInfo:
            self._tray_icon_activated()
        elif hotkey == self.Shuffle:
            self.shuffle()
        elif hotkey == self.PlayPause:
            self.play_pause()
        elif hotkey == self.NextTrack:
            self.next_track()
        elif hotkey == self.PrevTrack:
            self.previous_track()
        elif hotkey == self.Stop:
            self.stop()
    
    def global_key_down(self, event):
        id = event.KeyID
        #first check modifiers
        if id == win32con.VK_LSHIFT or id == win32con.VK_LSHIFT:
            self.modifiers |= win32con.MOD_SHIFT
        if id == win32con.VK_LCONTROL or id == win32con.VK_RCONTROL:
            self.modifiers |= win32con.MOD_CONTROL
        if id == win32con.VK_LMENU or id == win32con.VK_RMENU:
            self.modifiers |= win32con.MOD_ALT
        if id == win32con.VK_LWIN or id == win32con.VK_RWIN:
            self.modifiers |= win32con.MOD_WIN
        
        if self._check_hotkey(id, self.NextTrack, True):
            return False
        elif self._check_hotkey(id, self.PrevTrack, True):
            return False
        elif self._check_hotkey(id, self.PlayPause, True):
            return False
        elif self._check_hotkey(id, self.Stop, True):
            return False
        elif self._check_hotkey(id, self.ShowInfo, True):
            return False
        elif self._check_hotkey(id, self.Shuffle, True):
            return False
        return True
    
    def global_key_up(self, event):
        id = event.KeyID
        
        #first check modifiers...
        if id == win32con.VK_LSHIFT or id == win32con.VK_LSHIFT:
            self.modifiers &= ~win32con.MOD_SHIFT
        if id == win32con.VK_LCONTROL or id == win32con.VK_RCONTROL:
            self.modifiers &= ~win32con.MOD_CONTROL
        if id == win32con.VK_LMENU or id == win32con.VK_RMENU:
            self.modifiers &= ~win32con.MOD_ALT
        if id == win32con.VK_LWIN or id == win32con.VK_RWIN:
            self.modifiers &= ~win32con.MOD_WIN
        
        ignore = True
        if len(self.hotkeys_down) != 0: 
            for h in reversed(self.hotkeys_down):
                #if the key should no longer be considered pressed
                if h[0] == id or h[1] != self.modifiers:
                    h[2] = False
                    ignore = False
                    self.hotkeys_down.remove(h)
        return ignore 

def main(argv=None):
    #Connect to itunes
    print "Connecting to iTunes..."
    itunes.connect()
    
    #Open our system tray application
    print "Opening iShortcut..."
    app = QtGui.QApplication(sys.argv)
    tray = Systray()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

